import os
import cv2
import numpy as np
import face_recognition
import pickle
import threading
import time
from datetime import datetime
import sys
from pathlib import Path

# Add parent directory to path to import from other modules
sys.path.append(str(Path(__file__).parent.parent.parent))

from database.db_connection import execute_query, get_mysql_connection, close_mysql_connection
from backend.utils.logger import setup_logger

# Set up logger
logger = setup_logger('face_recognition')

class FaceRecognizer:
    # FaceRecognizer类的__init__() 方法用于初始化人脸识别系统，
    # 该方法的主要作用是设置识别参数、初始化数据结构、加载已知人脸信息。

    def __init__(self, camera_id=0, tolerance=0.5, frame_skip=2, callback=None):
        """
        Initialize the face recognition system.
        
        Args:
            camera_id (int): Camera device ID. Default is 0 (usually the built-in webcam).
            tolerance (float): Face recognition tolerance. Lower values are more strict.
            frame_skip (int): Process every nth frame to reduce CPU usage.
            callback (callable): Callback function to call when a face is recognized.
                The callback should accept employee_id and name as parameters.
        """
        self.camera_id = camera_id
        self.tolerance = tolerance  # 降低容差，提高严格度
        self.frame_skip = frame_skip
        self.known_face_encodings = []
        self.known_face_names = []
        self.known_employee_ids = []
        self.video_capture = None
        self.is_running = False
        self.frame_count = 0
        self.last_recognized_time = {}  # 记录上次识别时间，防止重复识别同一个人。
        self.recognition_cooldown = 5  # 降低识别冷却时间到5秒
        self.recognition_callback = callback
        self.face_detection_model = "hog"  # 添加人脸检测模型选择
        
        #数据库中 加载已知员工的脸部信息（编码、姓名、ID），供后续比对使用
        self.load_employee_faces()
    
    def _initialize_camera(self):
        """尝试多种方式初始化摄像头"""
        # 确保先释放之前的摄像头
        if self.video_capture is not None:
            self.video_capture.release()
            self.video_capture = None
            cv2.destroyAllWindows()
        
        # 等待一小段时间确保摄像头完全释放
        time.sleep(1)
        #定义摄像头后端
        camera_backends = [
            (cv2.CAP_DSHOW, "DirectShow"),
            (cv2.CAP_MSMF, "Media Foundation"),
            (cv2.CAP_ANY, "Auto")
        ]
        
        for backend, name in camera_backends:
            try:
                logger.info(f"尝试使用 {name} 后端初始化摄像头...")
                self.video_capture = cv2.VideoCapture(self.camera_id + backend)
                #设置摄像头分辨率这两个参数保证摄像头输出的图像是 640x480（标准分辨率）。
                self.video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
                self.video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
                
                if self.video_capture.isOpened():
                    # 设置摄像头属性
                    self.video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
                    self.video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
                    self.video_capture.set(cv2.CAP_PROP_FPS, 30)    #设置帧率为 30 FPS。
                    self.video_capture.set(cv2.CAP_PROP_AUTOFOCUS, 1)  #启用摄像头 自动对焦（部分摄像头可能不支持）
                    
                    # 读取一帧测试是否真正可用
                    ret, frame = self.video_capture.read()
                    if ret and frame is not None:
                        logger.info(f"成功使用 {name} 后端初始化摄像头")
                        return True
                
                # 如果读取失败，释放摄像头
                if self.video_capture is not None:
                    self.video_capture.release()
                    self.video_capture = None
                    cv2.destroyAllWindows()
                
            except Exception as e:
                logger.error(f"使用 {name} 后端初始化摄像头失败: {e}")
                if self.video_capture is not None:
                    self.video_capture.release()
                    self.video_capture = None
                    cv2.destroyAllWindows()
        
        return False
    #加载数据库中的员工人脸编码。
    def load_employee_faces(self):
        """Load employee face encodings from the database."""
        try:
            logger.info("Loading employee face encodings from database...")
            
            query = """
                SELECT employee_id, name, face_encoding 
                FROM employees 
                WHERE face_encoding IS NOT NULL
            """
            results = execute_query(query)
            
            self.known_face_encodings = []
            self.known_face_names = []
            self.known_employee_ids = []
            
            for row in results:
                # Convert the stored binary face encoding back to numpy array
                face_encoding = pickle.loads(row['face_encoding'])
                
                self.known_face_encodings.append(face_encoding)
                self.known_face_names.append(row['name'])
                self.known_employee_ids.append(row['employee_id'])
            
            logger.info(f"Loaded {len(self.known_face_encodings)} employee face encodings")
        except Exception as e:
            logger.error(f"Error loading employee face encodings: {e}")
#这段代码主要涉及 人脸识别系统的启动和停止，并使用 多线程 处理人脸识别过程
    def start(self, recognition_callback=None):
        """
        Start the face recognition process in a separate thread.

        Args:
            recognition_callback (callable): Callback function to call when a face is recognized.
                The callback should accept employee_id and name as parameters.
        """
        if self.is_running:
            logger.warning("Face recognition is already running")
            return

        # 初始化摄像头
        if not self._initialize_camera():
            logger.error("Failed to initialize camera")
            return

        self.is_running = True
        # Use the callback provided in start() if given, otherwise use the one from __init__
        if recognition_callback is not None:
            self.recognition_callback = recognition_callback

        # Start the face recognition thread
        self.thread = threading.Thread(target=self._run)
        self.thread.daemon = True
        self.thread.start()

        logger.info("Face recognition started")

    def stop(self):
        """Stop the face recognition process."""
        self.is_running = False
        if self.video_capture is not None:
            self.video_capture.release()
            self.video_capture = None
            cv2.destroyAllWindows()
        logger.info("Face recognition stopped")
   #该方法 _run(self) 负责人脸识别的核心循环，不断从摄像头捕获帧、检测人脸并进行匹配，并在匹配成功后触发回调函数。
    def _run(self):
        """Main face recognition loop."""
        try:
            if not self.video_capture or not self.video_capture.isOpened():
                logger.error(f"Camera is not properly initialized")
                self.is_running = False
                return
            
            logger.info(f"Face recognition loop started")
            
            # 创建支持中文的字体
            font_path = os.path.join(os.path.dirname(__file__), 'simhei.ttf')  # 确保此路径下有simhei.ttf字体文件
            if not os.path.exists(font_path):
                logger.warning("未找到中文字体文件，将使用默认字体")
                font_path = None
            
            while self.is_running:
                # Read a frame from the webcam
                ret, frame = self.video_capture.read()
                
                if not ret:
                    logger.error("Failed to capture frame from webcam")
                    break
                
                # Process every nth frame to reduce CPU usage
                self.frame_count += 1
                if self.frame_count % self.frame_skip != 0:
                    continue
                
                # 保持较高的分辨率以支持更大范围的人脸检测
                height, width = frame.shape[:2]
                if width > 1280:  # 使用更高的分辨率
                    scale = 1280.0 / width
                    frame = cv2.resize(frame, None, fx=scale, fy=scale)
                
                # Convert the frame from BGR (OpenCV format) to RGB (face_recognition format)
                rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                
                # 使用CNN模型进行人脸检测以提高准确性
                face_locations = face_recognition.face_locations(rgb_frame, model="hog")
                
                if face_locations:  # 只在检测到人脸时进行编码
                    # 使用较少的抖动次数进行实时编码
                    face_encodings = face_recognition.face_encodings(rgb_frame, face_locations, 
                                                                   num_jitters=1,
                                                                   model="small")
                    
                    # Process each face found in the frame
                    for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
                        # 计算人脸大小占比
                        face_height = bottom - top
                        face_width = right - left
                        frame_height, frame_width = frame.shape[:2]
                        face_area_ratio = (face_height * face_width) / (frame_height * frame_width)
                        
                        # 如果人脸太大（超过画面的70%）或太小（小于5%），跳过识别
                        if face_area_ratio > 0.7 or face_area_ratio < 0.05:
                            # 绘制未识别的人脸框（黄色）
                            cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 255), 2)
                            continue
                        
                        # 使用较低的容差进行匹配，提高准确性
                        matches = face_recognition.compare_faces(self.known_face_encodings, 
                                                              face_encoding, 
                                                              tolerance=self.tolerance)
                        
                        # 使用人脸距离计算最佳匹配
                        if True in matches and len(self.known_face_encodings) > 0:
                            face_distances = face_recognition.face_distance(self.known_face_encodings, face_encoding)
                            best_match_index = np.argmin(face_distances)
                            
                            if matches[best_match_index]:
                                name = self.known_face_names[best_match_index]
                                employee_id = self.known_employee_ids[best_match_index]
                                
                                # 检查识别冷却时间
                                current_time = time.time()
                                if employee_id in self.last_recognized_time:
                                    time_since_last = current_time - self.last_recognized_time[employee_id]
                                    if time_since_last < self.recognition_cooldown:
                                        continue
                                
                                # 更新最后识别时间
                                self.last_recognized_time[employee_id] = current_time
                                
                                # 记录考勤
                                self._record_attendance(employee_id)
                                
                                # 调用回调函数
                                if self.recognition_callback:
                                    self.recognition_callback(employee_id, name)
                                
                                # 在人脸周围绘制边框和标签（绿色）
                                cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
                                
                                # 创建半透明的背景
                                overlay = frame.copy()
                                cv2.rectangle(overlay, (left, bottom - 35), (right, bottom), (0, 255, 0), cv2.FILLED)
                                frame = cv2.addWeighted(overlay, 0.6, frame, 0.4, 0)
                                
                                # 使用默认字体显示文本（支持中文）
                                frame = cv2.putText(frame, name, (left + 6, bottom - 6),
                                                  cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
                            else:
                                # 绘制未知人脸的边框（红色）
                                cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
                                
                                # 创建半透明的背景
                                overlay = frame.copy()
                                cv2.rectangle(overlay, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
                                frame = cv2.addWeighted(overlay, 0.6, frame, 0.4, 0)
                                
                                # 显示"未知"文本
                                frame = cv2.putText(frame, "未知", (left + 6, bottom - 6),
                                                  cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
                        else:
                            # 绘制未知人脸的边框（红色）
                            cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
                            
                            # 创建半透明的背景
                            overlay = frame.copy()
                            cv2.rectangle(overlay, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
                            frame = cv2.addWeighted(overlay, 0.6, frame, 0.4, 0)
                            
                            # 显示"未知"文本
                            frame = cv2.putText(frame, "未知", (left + 6, bottom - 6),
                                              cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
                
                # Display the resulting frame
                cv2.imshow('Face Recognition', frame)
                
                # Exit loop if 'q' is pressed
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
            
            # Release the webcam and close all windows
            if self.video_capture is not None:
                self.video_capture.release()
            cv2.destroyAllWindows()
            
        except Exception as e:
            logger.error(f"Error in face recognition process: {e}")
            self.is_running = False

#记录员工的考勤情况，包括签到和签退，并且判断是否迟到或早退
    def _record_attendance(self, employee_id):
        """
        Record attendance for the recognized employee.
        
        Args:
            employee_id (str): The ID of the recognized employee.
        """
        try:
            # Get current date and time
            now = datetime.now()
            current_date = now.date()
            current_time = now.time()
            
            # Check if the employee has already checked in today
            query = """
                SELECT id, check_in_time, check_out_time 
                FROM attendance_records 
                WHERE employee_id = %s AND date = %s
            """
            results = execute_query(query, (employee_id, current_date))
            
            if not results:
                # Employee hasn't checked in today, record check-in
                logger.info(f"Recording check-in for employee {employee_id}")
                
                # Determine if the employee is late
                work_start_time = datetime.strptime("09:00:00", "%H:%M:%S").time()
                status = "late" if current_time > work_start_time else "normal"
                
                insert_query = """
                    INSERT INTO attendance_records 
                    (employee_id, check_in_time, date, status) 
                    VALUES (%s, %s, %s, %s)
                """
                execute_query(insert_query, (employee_id, now, current_date, status), fetch=False)
                
                logger.info(f"Check-in recorded for employee {employee_id}")
                return "check_in"
            else:
                # Employee has already checked in
                record = results[0]
                
                if record['check_out_time'] is None:
                    # Employee hasn't checked out yet, record check-out
                    logger.info(f"Recording check-out for employee {employee_id}")
                    
                    # Determine if the employee is leaving early
                    work_end_time = datetime.strptime("18:00:00", "%H:%M:%S").time()
                    
                    update_query = """
                        UPDATE attendance_records 
                        SET check_out_time = %s
                        WHERE id = %s
                    """
                    execute_query(update_query, (now, record['id']), fetch=False)
                    
                    # If employee is leaving early, update status
                    if current_time < work_end_time:
                        status_query = """
                            UPDATE attendance_records 
                            SET status = 'early_leave'
                            WHERE id = %s AND status = 'normal'
                        """
                        execute_query(status_query, (record['id'],), fetch=False)
                    
                    logger.info(f"Check-out recorded for employee {employee_id}")
                    return "check_out"
                else:
                    # Employee has already checked out, do nothing
                    logger.info(f"Employee {employee_id} has already checked in and out today")
                    return "already_checked"
        
        except Exception as e:
            logger.error(f"Error recording attendance: {e}")
            return "error"
    #该函数用于注册员工的人脸信息，将人脸特征存入数据库，并更新已存储的员工人脸数据。
    def register_face(self, employee_id, name, department, position, image_data=None):
        """
        进行人脸注册
        Returns:
            tuple: (bool, str) - 注册是否成功，以及对应的信息
        """

        def process_registration():
            try:
                if image_data is None:
                    logger.error("未提供图像数据")
                    return False, "未提供图像数据"

                # 将图像数据转换为numpy数组
                nparr = np.frombuffer(image_data, np.uint8)

                # 用 OpenCV 解码图片
                frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)

                if frame is None:
                    logger.error("无法解码图像数据")
                    return False, "无法解码图像数据"

                # 调整图像大小，但保持较高的分辨率以提高质量
                height, width = frame.shape[:2]
                if width > 1280:  # 增加最大宽度到1280
                    scale = 1280.0 / width
                    frame = cv2.resize(frame, None, fx=scale, fy=scale)

                # 转换为RGB格式
                rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

                # 使用CNN模型进行人脸检测，提高准确性
                face_locations = face_recognition.face_locations(rgb_frame, model="cnn")

                # 如果检测到多张人脸，仅保留最大的一张
                if len(face_locations) > 1:
                    face_locations.sort(key=lambda x: (x[2] - x[0]) * (x[3] - x[1]), reverse=True)
                    face_locations = face_locations[:1]

                if len(face_locations) == 0:
                    logger.error("未检测到人脸")
                    return False, "未检测到人脸"

                # 提高人脸编码质量
                face_encodings = face_recognition.face_encodings(rgb_frame, face_locations, 
                                                               num_jitters=5,  # 增加抖动次数
                                                               model="large")  # 使用大模型提高准确性

                if len(face_encodings) > 0:
                    face_encoding = face_encodings[0]

                    # 使用更严格的容差进行重复检查
                    if len(self.known_face_encodings) > 0:
                        matches = face_recognition.compare_faces(self.known_face_encodings, face_encoding,
                                                              tolerance=0.5)  # 降低容差提高严格度
                        if True in matches:
                            logger.error("该人脸已经注册")
                            return False, "该人脸已经注册"

                    # 序列化人脸数据
                    face_encoding_binary = pickle.dumps(face_encoding)

                    try:
                        # 存入数据库
                        query = """
                            INSERT INTO employees 
                            (employee_id, name, department, position, face_encoding, face_registered) 
                            VALUES (%s, %s, %s, %s, %s, 1)
                            ON DUPLICATE KEY UPDATE 
                            name = VALUES(name),
                            department = VALUES(department),
                            position = VALUES(position),
                            face_encoding = VALUES(face_encoding),
                            face_registered = 1
                        """
                        execute_query(query, (employee_id, name, department, position, face_encoding_binary),
                                      fetch=False)

                        # 重新加载人脸数据
                        self.load_employee_faces()

                        success_msg = f"成功注册员工 {name} (ID: {employee_id}) 的人脸信息"
                        logger.info(success_msg)
                        return True, success_msg

                    except Exception as e:
                        error_msg = f"数据库操作失败: {e}"
                        logger.error(error_msg)
                        return False, error_msg
                else:
                    logger.error("无法提取人脸特征")
                    return False, "无法提取人脸特征"

            except Exception as e:
                error_msg = f"人脸注册过程中出错: {e}"
                logger.error(error_msg)
                return False, error_msg

                # **同步执行** 确保返回值
        return process_registration()
        #threading.Thread(target=process_registration)
        #thread.start()
        #thread.join()  # 等待线程执行完毕
        #return process_registration()  # 确保返回值是一个元组


# For testing
if __name__ == "__main__":
    def recognition_callback(employee_id, name):
        print(f"Recognized: {name} (ID: {employee_id})")
    
    recognizer = FaceRecognizer()
    recognizer.start(recognition_callback=recognition_callback)
    
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        recognizer.stop()
        print("Face recognition stopped") 